package io.kiki.stack.netty;

import io.kiki.stack.netty.config.*;
import io.kiki.stack.netty.config.configs.ConfigContainer;
import io.kiki.stack.netty.config.configs.DefaultConfigContainer;
import io.kiki.stack.netty.config.switches.GlobalSwitch;
import io.kiki.stack.netty.lifeCycle.AbstractLifeCycle;
import io.kiki.stack.netty.lifeCycle.LifeCycleException;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.net.InetSocketAddress;


@Slf4j
@Setter
@Getter
public abstract class AbstractServer extends AbstractLifeCycle implements IServer, Configurable {


    private final Options options;
    private final GlobalSwitch globalSwitch;
    private final ConfigContainer configContainer;
    private final String ip;
    private int port;

    public AbstractServer(int port) {
        this(new InetSocketAddress(port).getAddress().getHostAddress(), port);
    }

    public AbstractServer(String ip, int port) {
        if (port < 0 || port > 65535) {
            throw new IllegalArgumentException(String.format("Illegal port value: %d, which should between 0 and 65535.", port));
        }
        this.ip = ip;
        this.port = port;

        this.options = new Options();
        this.globalSwitch = new GlobalSwitch();
        this.configContainer = new DefaultConfigContainer();
    }

    @Override
    @Deprecated
    public void init() {
        // Do not call this method, it will be removed in the next version
    }

    @Override
    @Deprecated
    public boolean start() {
        startup();
        return true;
    }

    @Override
    @Deprecated
    public boolean stop() {
        shutdown();
        return true;
    }

    @Override
    public void startup() throws LifeCycleException {
        super.startup();

        try {
            doInit();

            log.warn("Prepare to start server on port {} ", port);
            if (doStart()) {
                log.warn("Server started on port {}", port);
            } else {
                log.warn("Failed starting server on port {}", port);
                throw new LifeCycleException("Failed starting server on port: " + port);
            }
        } catch (Throwable t) {
            this.shutdown();// do stop to ensure close resources created during doInit()
            throw new IllegalStateException("ERROR: Failed to start the Server!", t);
        }
    }

    @Override
    public void shutdown() throws LifeCycleException {
        super.shutdown();
        if (!doStop()) {
            throw new LifeCycleException("doStop fail");
        }
    }

    @Override
    public String ip() {
        return ip;
    }

    @Override
    public int port() {
        return port;
    }

    /**
     * override the random port zero with the actual binding port value.
     *
     * @param port local binding port
     */
    protected void setLocalBindingPort(int port) {
        if (port() == 0) {
            this.port = port;
        }
    }

    protected abstract void doInit();

    protected abstract boolean doStart() throws InterruptedException;

    protected abstract boolean doStop();

    @Override
    public <T> T option(Option<T> option) {
        return options.option(option);
    }

    @Override
    public <T> Config option(Option<T> option, T value) {
        options.option(option, value);
        return this;
    }

    @Override
    @Deprecated
    public ConfigContainer conf() {
        return this.configContainer;
    }

    @Override
    @Deprecated
    public GlobalSwitch switches() {
        return this.globalSwitch;
    }

    @Override
    public void initWriteBufferWaterMark(int low, int high) {
        option(ServerOption.netty_buffer_low_water_mark, low);
        option(ServerOption.netty_buffer_high_water_mark, high);
    }

    @Override
    public int netty_buffer_low_watermark() {
        return option(ServerOption.netty_buffer_low_water_mark);
    }

    @Override
    public int netty_buffer_high_watermark() {
        return option(ServerOption.netty_buffer_high_water_mark);
    }
}
